////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2012 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

#include <tinyhal.h>
#include "..\FM4.h"
#include "FM4_I2C_driver.h"

#pragma optimize("", off)
#pragma GCC optimization_level 0
#pragma GCC optimize ("O")

//#define DEBUG_I2C

//void CPU_INTC_Priority(UINT32 Irq_Index, UINT32 Irq_Pri);

//BOOL m_FM4_I2C_Driver_Initialized = FALSE;
FM4_I2C_Driver g_FM4_I2C_Driver[FM4_I2C_Driver::c_MaxCh];

FM4_MFS_I2C_TypeDef *g_I2C[8] = {
    (FM4_MFS_I2C_TypeDef *)(FM4_MFS0),
    (FM4_MFS_I2C_TypeDef *)(FM4_MFS1),
    (FM4_MFS_I2C_TypeDef *)(FM4_MFS2),
    (FM4_MFS_I2C_TypeDef *)(FM4_MFS3),
    (FM4_MFS_I2C_TypeDef *)(FM4_MFS4),
    (FM4_MFS_I2C_TypeDef *)(FM4_MFS5),
    (FM4_MFS_I2C_TypeDef *)(FM4_MFS6),
    (FM4_MFS_I2C_TypeDef *)(FM4_MFS7)
};

#if defined(MB9BF568R)
const UINT8 I2C_SDA[] = {
    P14,    // SDA0_1
    P09,    // SDA1_0
    P73,    // SDA2_0
    P67,    // SDA3_0
    P1A,    // SDA4_1
    P64,    // SDA5_1
    P56,    // SDA6_0
    P4D     // SDA7_1
};

const UINT8 I2C_SCL[] = {
    P15,
    P08,
    P74,
    P68,
    P1B,
    P65,
    P57,
    P4C
};
#else
#error "CPU type not defined. Please define MB9BF568R!"
#endif

#if 0
// I2C Idx
// 0   0
// 1   1
// 2   2
// 3   3
// 4   4
// 5   5
// 6   6
// 7   7
int IntIdxToI2C[8] = {0, 1, 2, 3, 4, 5, 6, 7};
int I2CToIntIdx[8] = {0, 1, 2, 3, 4, 5, 6, 7};
#endif

void WriteToSubordinate(UINT32 ch, I2C_HAL_XACTION_UNIT *unit)
{
    FM4_MFS_I2C_TypeDef *i2c = g_I2C[ch];
    UINT8 *queueData = unit->m_dataQueue.Pop();
    ASSERT(queueData);
#ifdef DEBUG_I2C
    lcd_printf("W:%02x ", *queueData);
#endif
    i2c->i2c03.TDR = *queueData;
    ++unit->m_bytesTransferred;
    --unit->m_bytesToTransfer;
}

void ReadFromSubordinate(UINT32 ch, I2C_HAL_XACTION_UNIT *unit)
{
    FM4_MFS_I2C_TypeDef *i2c = g_I2C[ch];
    UINT8* queueData;
    queueData = unit->m_dataQueue.Push();
    ASSERT(queueData);
    UINT8 data = i2c->i2c03.RDR;
#ifdef DEBUG_I2C
    lcd_printf("R:%02x ", data);
#endif
    *queueData = data;
    ++unit->m_bytesTransferred;
    --unit->m_bytesToTransfer;
}

void FM4_I2C_Handle_IRQ(UINT32 ch)
{
    INTERRUPT_START;
    GLOBAL_LOCK(irq);
    UINT8 sla;
    BOOL isRead;
    FM4_MFS_I2C_TypeDef *i2c = g_I2C[ch];
    UINT8 ibcr = i2c->i2c03.IBCR;
    UINT8 ibsr = i2c->i2c03.IBSR;
    I2C_HAL_XACTION* xAction = g_FM4_I2C_Driver[ch].m_currentXAction;
    I2C_HAL_XACTION_UNIT* unit = g_FM4_I2C_Driver[ch].m_currentXActionUnit;
    isRead = unit->IsReadXActionUnit();
    size_t len = unit->m_bytesToTransfer;
    if (ibcr & IBCR_BER) {
#ifdef DEBUG_I2C
        lcd_printf("I2C ERR IBCR=%02X IBSR=%02X\r\n", ibcr, ibsr);
#endif
        //ASSERT(0);
        i2c->i2c03.IBCR &= ~(IBCR_INT);
        xAction->Signal(I2C_HAL_XACTION::c_Status_Aborted);
    }
    if (ibsr & IBSR_SPC) {
        i2c->i2c03.IBSR &= ~IBSR_SPC;
        i2c->i2c03.IBCR &= ~(IBCR_CNDE | IBCR_SCC);
    } else
    if (!isRead) {
            if (ibsr & IBSR_RACK) {
#ifdef DEBUG_I2C
                lcd_printf("I2C ERR TNACK IBCR=%02X IBSR=%02X\r\n", ibcr, ibsr);
#endif
//              i2c->i2c03.IBCR &= ~(IBCR_MSS | IBCR_SCC| IBCR_INTE | IBCR_INT);
//              i2c->i2c03.IBCR |= (IBCR_CNDE);
                i2c->i2c03.IBCR &= ~(IBCR_INT);
                xAction->Signal(I2C_HAL_XACTION::c_Status_Aborted);
            } else {
                if (len > 0) {
                    // write
                    WriteToSubordinate(ch, unit);
                    i2c->i2c03.IBCR =       // 0x84
                            IBCR_MSS |      // select master
                            IBCR_INTE;      // enable interrupt
                } else if (len == 0) {
                    if (xAction->ProcessingLastUnit()) {
                        xAction->Signal(I2C_HAL_XACTION::c_Status_Completed);
                    } else {
                        FM4_I2C_Driver::MasterXAction_Start(ch, xAction, true);
                    }
                }
            }
        } else {
            // read
            if (ibsr & IBSR_RSC) {
                i2c->i2c03.IBSR &= ~IBSR_RSC;
            }
            if (ibsr & IBSR_RACK) {
                xAction->Signal(I2C_HAL_XACTION::c_Status_Completed);
            } else if (ibsr & IBSR_FBT) {
                i2c->i2c03.IBCR =       // 0xA4
                        IBCR_MSS |      // select master
                        IBCR_ACKE |     // invoke "L" as acknowledge
                        IBCR_INTE;      // enable interrupt
            } else {
                if (len > 1) {
                    ReadFromSubordinate(ch, unit);
                    i2c->i2c03.IBCR =       // 0xA4
                            IBCR_MSS |      // select master
                            IBCR_ACKE |     // invoke "L" as acknowledge
                            IBCR_INTE;      // enable interrupt
                } else if (len == 1) {
                    ReadFromSubordinate(ch, unit);
                    i2c->i2c03.IBCR =       // 0x84
                            IBCR_MSS |      // select master
                            IBCR_INTE;      // enable interrupt
                } else {
                    i2c->i2c03.IBCR &= ~(IBCR_INT);
                    xAction->Signal(I2C_HAL_XACTION::c_Status_Aborted);
                }
            }
        }
    INTERRUPT_END
}

void I2C0RX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(0);
}

void I2C1RX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(1);
}

void I2C2RX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(2);
}

void I2C3RX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(3);
}

void I2C4RX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(4);
}

void I2C5RX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(5);
}

void I2C6RX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(6);
}

void I2C7RX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(7);
}

void I2C0TX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(0);
}

void I2C1TX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(1);
}

void I2C2TX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(2);
}

void I2C3TX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(3);
}

void I2C4TX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(4);
}

void I2C5TX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(5);
}

void I2C6TX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(6);
}

void I2C7TX_IRQHandler(void *param)
{
    FM4_I2C_Handle_IRQ(7);
}

MFSTX_IRQHandler *I2CTX_IRQHandler[8] = {
    (MFSTX_IRQHandler *)I2C0TX_IRQHandler,
    (MFSTX_IRQHandler *)I2C1TX_IRQHandler,
    (MFSTX_IRQHandler *)I2C2TX_IRQHandler,
    (MFSTX_IRQHandler *)I2C3TX_IRQHandler,
    (MFSTX_IRQHandler *)I2C4TX_IRQHandler,
    (MFSTX_IRQHandler *)I2C5TX_IRQHandler,
    (MFSTX_IRQHandler *)I2C6TX_IRQHandler,
    (MFSTX_IRQHandler *)I2C7TX_IRQHandler
};

MFSRX_IRQHandler *I2CRX_IRQHandler[8] = {
    (MFSRX_IRQHandler *)I2C0RX_IRQHandler,
    (MFSRX_IRQHandler *)I2C1RX_IRQHandler,
    (MFSRX_IRQHandler *)I2C2RX_IRQHandler,
    (MFSRX_IRQHandler *)I2C3RX_IRQHandler,
    (MFSRX_IRQHandler *)I2C4RX_IRQHandler,
    (MFSRX_IRQHandler *)I2C5RX_IRQHandler,
    (MFSRX_IRQHandler *)I2C6RX_IRQHandler,
    (MFSRX_IRQHandler *)I2C7RX_IRQHandler
};

BOOL FM4_I2C_Driver::Initialize(UINT32 ch)
{
    GLOBAL_LOCK(irq);
    FM4_MFS_I2C_TypeDef *i2c = g_I2C[ch];
    if (ch == 1) {
        g_FM4_I2C_Driver[ch].m_initialized  = TRUE;
        g_FM4_I2C_Driver[ch].m_currentXAction = NULL;
        g_FM4_I2C_Driver[ch].m_currentXActionUnit = NULL;
        i2c->i2c03.ISMK &= 0x7f;    // Disable
        i2c->i2c03.SMR = 0x80;
        i2c->i2c03.SSR = 0;
        i2c->i2c03.BGR = F_PCLK/I2C_BPS - 3;
        i2c->i2c03.ISBA = 0;    // Doesn't compare slave address
        switch (ch) {
        case 1:
            FM4_GPIO->EPFR07 = (FM4_GPIO->EPFR07 & ~(0x0f << 12)) | (5 << 12);
            FM4_GPIO->PFR0 |= (3 << 8);        // CH1-0 P08, P09
            FM4_GPIO->PCR0 &= ~(3 << 8);
            break;
        default:
            break;
        }
        CPU_INTC_ActivateInterrupt(MFS0_RX_IRQn + ch*2, I2CRX_IRQHandler[ch], 0);
        CPU_INTC_ActivateInterrupt(MFS0_TX_IRQn + ch*2, I2CTX_IRQHandler[ch], 0);
        //CPU_INTC_Priority(MFS0RX_IRQn + ch*2, 2);
        //CPU_INTC_Priority(MFS0TX_IRQn + ch*2, 2);
        i2c->i2c03.ISMK |= 0x80;    // Enable
        return TRUE;
    } else
        return FALSE;
}

BOOL FM4_I2C_Driver::Uninitialize(UINT32 ch)
{
    GLOBAL_LOCK(irq);
    if (ch == 1) {
        g_FM4_I2C_Driver[ch].m_initialized = FALSE;
        FM4_MFS_I2C_TypeDef *i2c = g_I2C[ch];
        i2c->i2c03.ISMK &= 0x7f;    // Disable
        CPU_INTC_DeactivateInterrupt(MFS0_RX_IRQn + ch*2);
        CPU_INTC_DeactivateInterrupt(MFS0_TX_IRQn + ch*2);
    }
    return TRUE;
}

void FM4_I2C_Driver::MasterXAction_Start(UINT32 ch, I2C_HAL_XACTION *xAction, bool repeatedStart)
{
    NATIVE_PROFILE_HAL_PROCESSOR_I2C();
    UINT8 sla;
    BOOL isRead;
    g_FM4_I2C_Driver[ch].m_currentXAction = xAction;
    g_FM4_I2C_Driver[ch].m_currentXActionUnit = xAction->m_xActionUnits[xAction->m_current++];
    isRead = g_FM4_I2C_Driver[ch].m_currentXActionUnit->IsReadXActionUnit();
    FM4_MFS_I2C_TypeDef *i2c = g_I2C[ch];
    i2c->i2c03.BGR = (UINT16)(xAction->m_clockRate) + ((UINT16)(xAction->m_clockRate2) << 8);
    sla = (xAction->m_address << 1) | (isRead ? 1:0);
#ifdef DEBUG_I2C
    if (repeatedStart)
        lcd_printf("I2C RS SLA=%02X\r\n", sla);
    else
        lcd_printf("I2C ST SLA=%02X\r\n", sla);
#endif
    i2c->i2c03.TDR = sla;
    if (repeatedStart) {
        i2c->i2c03.IBCR =   // 0xc5
                IBCR_MSS |  // select master
                IBCR_SCC |  // invoke repeated-start condition
                IBCR_INTE | // enable interrupt
                IBCR_INT;   // set interrupt flag
    } else {
        i2c->i2c03.IBCR =   //
                IBCR_MSS |  // select master
                IBCR_INTE | // enable interrupt
                IBCR_INT;   // set interrupt
    }
}

void FM4_I2C_Driver::MasterXAction_Stop(UINT32 ch)
{
    NATIVE_PROFILE_HAL_PROCESSOR_I2C();
    FM4_MFS_I2C_TypeDef *i2c = g_I2C[ch];
    // cause stop condition
    i2c->i2c03.IBCR &= ~(
            IBCR_MSS |      // clear master
            IBCR_SCC |      // clear repeated-start condition
            IBCR_INTE |     // disable interrupt
            IBCR_INT);      // clear interrupt flag
    g_FM4_I2C_Driver[ch].m_currentXAction = NULL;
    g_FM4_I2C_Driver[ch].m_currentXActionUnit = NULL;
#ifdef DEBUG_I2C
    lcd_printf("I2C Stop                     \r\n");
#endif
}

void FM4_I2C_Driver::GetClockRate(UINT32 ch, UINT32 rateKhz, UINT8& clockRate, UINT8& clockRate2)
{
    UINT32 bgr;
    if (rateKhz > 400)
        rateKhz = 400;
    if (rateKhz == 0)
        rateKhz = 1;
    rateKhz *= 1000;
    bgr = F_PCLK/rateKhz - 3;
    clockRate = (UINT8)(bgr & 0xff);
    clockRate2 = (UINT8)(bgr >> 8);
}

void FM4_I2C_Driver::GetPins(UINT32 ch, GPIO_PIN& scl, GPIO_PIN& sda)
{
    NATIVE_PROFILE_HAL_PROCESSOR_I2C();
    scl = (GPIO_PIN)I2C_SCL[ch];
    sda = (GPIO_PIN)I2C_SDA[ch];
}
